#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include "comm.h"

void *pthread_proc_smtp(void *arg)
{
	int proc_fd = *(int *)arg;
	int proc_ret;
	int flag = 0;
	char buffer[128] = {0};
	char response[128] = {0};
	struct sockaddr_in client_addr;
	struct timeval timeout;
	struct mailbox mail;
	fd_set read_set = {0};
	socklen_t client_len = sizeof(client_addr);
	getpeername(proc_fd, (struct sockaddr *)&client_addr, &client_len);

	// 分离线程
	pthread_detach(pthread_self());

	// 获取客户端ip
	char client_ip[32] = {0};
	inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, client_ip, sizeof(client_ip));

	// 具体流程如下
	while (1)
	{
		FD_ZERO(&read_set);
		FD_SET(proc_fd, &read_set);

		timeout.tv_sec = 5;
		timeout.tv_usec = 0;

		proc_ret = select(proc_fd + 1, &read_set, NULL, NULL, &timeout);
		if (proc_ret < 0)
		{
			perror("select failed");
			continue;
		}

		if (FD_ISSET(proc_fd, &read_set))
		{
			// 读取客户端数据
			memset(buffer, 0, sizeof(buffer));
			proc_ret = recv(proc_fd, buffer, sizeof(buffer) - 1, 0);
			if (proc_ret < 0)
			{
				perror("recv failed");
				close(proc_fd);
				return NULL;
			}

			if (strncasecmp(buffer, "EHLO", 4) == 0)
			{
				char help_buf[128] = {0};
				snprintf(help_buf, sizeof(help_buf),
						 "250-%s\r\n250-AUTH LOGIN PLAIN\r\n250-AUTH=LOGIN\r\n250 8BITMIME\r\n",
						 client_ip);
				send(proc_fd, help_buf, sizeof(help_buf), 0);
			}
			else if (strncasecmp(buffer, "AUTH LOGIN", 10) == 0)
			{
				strcpy(response, "334 VXNlcm5hbWU6\r\n");
				send(proc_fd, response, sizeof(response), 0);

				memset(buffer, 0, sizeof(buffer));
				recv(proc_fd, buffer, sizeof(buffer) - 1, 0);
				if (strncmp(buffer, USERNAME, strlen(USERNAME)) == 0)
				{
					strcpy(response, "334 UGFzc3dvcmQ6\r\n");
					send(proc_fd, response, sizeof(response), 0);

					memset(buffer, 0, sizeof(buffer));
					recv(proc_fd, buffer, sizeof(buffer) - 1, 0);
					if (strncmp(buffer, AUTHCODE, strlen(AUTHCODE)) == 0)
					{
						strcpy(response, "235 Authorization successful\r\n");
						send(proc_fd, response, sizeof(response), 0);

						flag = 1;
					}
					else
					{
						send(proc_fd, "500 Command not recognized\r\n", 28, 0);
					}
				}
				else
				{
					send(proc_fd, "500 Command not recognized\r\n", 28, 0);
				}
			}
			else if (strncasecmp(buffer, "quit", 4) == 0)
			{
				send(proc_fd, "221 Bye\r\n", 9, 0);
				break;
			}
			else
			{
				send(proc_fd, "500 Command not recognized\r\n", 28, 0);
			}

			while (flag)
			{
				memset(buffer, 0, sizeof(buffer));
				recv(proc_fd, buffer, sizeof(buffer) - 1, 0);

				if (strncmp(buffer, "MAIL FROM:", 10) == 0)
				{
					memset(mail.send_addr, 0, sizeof(mail.send_addr));
					sscanf(buffer, "MAIL FROM:%63s", mail.send_addr);
					send(proc_fd, "250 OK\r\n", 8, 0);
				}
				else if (strncmp(buffer, "RCPT TO:", 8) == 0)
				{
					memset(mail.recv_addr, 0, sizeof(mail.recv_addr));
					sscanf(buffer, "RCPT TO:%63s", mail.recv_addr);
					send(proc_fd, "250 OK\r\n", 8, 0);
				}
				else if (strncmp(buffer, "Subject:", 8) == 0)
				{
					memset(mail.subject, 0, sizeof(mail.subject));
					sscanf(buffer, "Subject:%63s", mail.subject);
					send(proc_fd, "250 OK\r\n", 8, 0);
				}
				else if (strncasecmp(buffer, "DATA", 4) == 0)
				{
					strcpy(response, "354 Start mail input; end data with<CR><LF>.<CR><LF>\r\n");
					memset(mail.mail_data, 0, sizeof(mail.mail_data));

					while (1)
					{
						proc_ret = send(proc_fd, response, sizeof(response), 0);
						if (proc_ret < 0)
						{
							perror("data send failed");
							continue;
						}
						memset(response, 0, sizeof(response));

						memset(buffer, 0, sizeof(buffer));
						recv(proc_fd, buffer, sizeof(buffer), 0);

						if (strncmp(buffer, ".\r\n", 3) == 0)
							break;

						strncat(mail.mail_data, buffer,
								sizeof(mail.mail_data) - strlen(mail.mail_data) - 1);
					}

					FILE *fp = fopen(MAILNAME, "a");
					if (fp)
					{
						fprintf(fp, "From: %s\nTo: %s\nSubject: %s\nData: \n%s\n",
								mail.send_addr, mail.recv_addr, mail.subject, mail.mail_data);

						fclose(fp);
						send(proc_fd, "250 Save success\r\n", 18, 0);
					}
					else
					{
						send(proc_fd, "552 Mailbox unavailable\r\n", 28, 0);
					}
				}
				else if (strncasecmp(buffer, "quit", 4) == 0)
				{
					send(proc_fd, "221 Bye\r\n", 9, 0);
					close(proc_fd);
					return NULL;
				}
				else
				{
					send(proc_fd, "500 Command not recognized\r\n", 28, 0);
				}
			}
		}
	}

	close(proc_fd);
	return NULL;
}

int main()
{
	int socket_fd, new_fd;
	int ret;
	char buf[128] = {0};
	struct sockaddr_in server_addr;
	struct sockaddr_in client_addr;
	socklen_t addrlen = sizeof(client_addr);

	// 创建套接字
	socket_fd = socket(AF_INET, SOCK_STREAM, 0);
	if (socket_fd < 0)
	{
		perror("socket failed");
		return -1;
	}

	// 设置地址重用属性
	int reuse = 1;
	setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));

	// 绑定端口
	memset(&server_addr, 0, sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.s_addr = INADDR_ANY;
	server_addr.sin_port = htons(SMTP_PORT);

	ret = bind(socket_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
	if (ret < 0)
	{
		perror("bind failed");
		return -1;
	}

	// 监听端口
	ret = listen(socket_fd, 5);
	if (ret < 0)
	{
		perror("listen failed");
		return -1;
	}
	printf("smtp server listening on port: %d\n", SMTP_PORT);

	while (1)
	{
		// 接受新的连接
		memset(&client_addr, 0, sizeof(client_addr));
		new_fd = accept(socket_fd, (struct sockaddr *)&client_addr, &addrlen);
		if (new_fd < 0)
		{
			perror("accept failed");
			continue;
		}

		// 发送欢迎消息
		char welcome[128] = {0};
		inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, buf, sizeof(buf));
		snprintf(welcome, sizeof(welcome), "220 %s Esmtp QQ Mail Server\r\n", buf);
		ret = send(new_fd, welcome, sizeof(welcome), 0);
		if (ret < 0)
		{
			perror("welcome send failed");
			continue;
		}

		// 创建线程
		pthread_t tid;
		pthread_create(&tid, NULL, pthread_proc_smtp, (void *)&new_fd);
	}

	close(socket_fd);
	return 0;
}
